package com.tianyu.inspection.service.telemetry.yukong.tcp;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tianyu.inspection.common.ByteArray;
import com.tianyu.inspection.common.Constants;
import com.tianyu.inspection.common.component.AppConstants;
import com.tianyu.inspection.common.enums.FileTypeEnum;
import com.tianyu.inspection.common.enums.FlightState;
import com.tianyu.inspection.common.enums.TaskSource;
import com.tianyu.inspection.common.model.FlyTaskModel;
import com.tianyu.inspection.common.model.JedisModel;
import com.tianyu.inspection.common.protobuf.Telemetry;
import com.tianyu.inspection.common.protobuf.YuKongBusinessData;
import com.tianyu.inspection.common.protobuf.YuKongTelemetry;
import com.tianyu.inspection.entity.FlyTask;
import com.tianyu.inspection.entity.Project;
import com.tianyu.inspection.entity.UploadFile;
import com.tianyu.inspection.entity.model.QueryMo;
import com.tianyu.inspection.mapper.FlyTaskMapper;
import com.tianyu.inspection.mapper.PipelineMapper;
import com.tianyu.inspection.mapper.UploadFileMapper;
import com.tianyu.inspection.service.api.IProjectService;
import com.tianyu.inspection.service.telemetry.yukong.YkClient;
import com.tianyu.inspection.service.websocket.TelemetryQueue;
import com.tianyu.inspection.service.websocket.WebSocketService;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.ocs.model.OcsObject;
import io.ocs.model.OcsSimpleModel;
import lombok.extern.slf4j.Slf4j;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

import static com.tianyu.inspection.service.telemetry.yukong.YkClient.YK_CHANNEL_MAP;

/**
 * @author swyan
 */
@Slf4j
public class YkTcpClientChannelHandler extends SimpleChannelInboundHandler<byte[]> {

    private final Integer projectId;
    private final String token;
    private final JedisModel jedisModel;
    private final String ykAccessKey;
    private final String ykAccessSecret;
    private final WebSocketService webSocketService;
    private final FlyTaskMapper flyTaskMapper;
    private final PipelineMapper pipelineMapper;
    private final UploadFileMapper uploadFileMapper;
    private final IProjectService projectService;
    private final OcsSimpleModel ocsSimpleModel;
    private final AppConstants appConstants;

    private final AtomicInteger heartBeatLog = new AtomicInteger(20);

    public YkTcpClientChannelHandler(int projectId, String token, JedisModel jedisModel,
                                     String ykAccessKey,
                                     String ykAccessSecret,
                                     WebSocketService webSocketService,
                                     FlyTaskMapper flyTaskMapper,
                                     PipelineMapper pipelineMapper,
                                     UploadFileMapper uploadFileMapper,
                                     IProjectService projectService, OcsSimpleModel ocsSimpleModel,
                                     AppConstants appConstants) {
        this.projectId = projectId;
        this.token = token;
        this.jedisModel = jedisModel;
        this.ykAccessKey = ykAccessKey;
        this.ykAccessSecret = ykAccessSecret;
        this.webSocketService = webSocketService;
        this.flyTaskMapper = flyTaskMapper;
        this.pipelineMapper = pipelineMapper;
        this.uploadFileMapper = uploadFileMapper;
        this.projectService = projectService;
        this.ocsSimpleModel = ocsSimpleModel;
        this.appConstants = appConstants;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, byte[] bytes) throws Exception {
        try {
            switch (bytes[0] & 0xFF) {
                case 0x01: {
                    if (bytes[5] == 0x00) {
                        log.debug("Project[" + projectId + "] Tcp-Client[御空] Successful Registered...");
                    } else if (bytes[5] == 0x01) {
                        log.debug("Project[" + projectId + "] Tcp-Client[御空] Registered Token Error, 2s后重试...");
                    } else {
                        log.debug("Project[" + projectId + "] Tcp-Client[御空] Registered Fail, 状态码[" + bytes[5] + "]...");
                    }
                }
                break;
                case 0x02: {
                    final int i = heartBeatLog.getAndDecrement();
                    if (i == 0) {
                        log.info("Project[" + projectId + "] Tcp-Client[御空] HeartBeat...");
                        heartBeatLog.set(20);
                    }
                }
                break;
                case 0x03: {
                    byte[] newBytes = new byte[bytes.length - 5];
                    System.arraycopy(bytes, 5, newBytes, 0, bytes.length - 5);
                    log.debug("Project[" + projectId + "] Tcp-Client[御空] Token过期[{}] 连接关闭...", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(
                            new Date((long) ByteArray.convertBigEndian(new ByteArray(newBytes).getInt64(0)))));
                    YK_CHANNEL_MAP.remove(projectId).close();
                }
                break;
                case 0x04: {
                    dealTelemetry(bytes);
                }
                break;
                case 0x05: {
                    dealTaskMessage(bytes);
                }
                break;
                default: {
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.debug("Tcp读取数据异常！");
        }
    }

    private void dealTelemetry(byte[] bytes) throws com.google.protobuf.InvalidProtocolBufferException {
        byte[] newBytes = new byte[bytes.length - 5];
        System.arraycopy(bytes, 5, newBytes, 0, bytes.length - 5);
        YuKongTelemetry.Data telemetryData = YuKongTelemetry.Data.parseFrom(newBytes);
        log.info("[" + projectId + "][YK] taskId: " + telemetryData.getTaskId() + " boxSn: " + telemetryData.getBoxSn());
        Telemetry.LiveData.Builder builder = Telemetry.LiveData.newBuilder();
        builder.setTaskId(String.valueOf(telemetryData.getTaskId()));
        builder.setUavId(!"".equals(telemetryData.getUavNum()) ? telemetryData.getUavNum() : telemetryData.getBoxSn());
        builder.setUavName(telemetryData.getTaskName());
        builder.setProjectId(projectId);
        builder.setLatitude(telemetryData.getLat());
        builder.setLongitude(telemetryData.getLng());
        builder.setPitch(telemetryData.getPitch());
        builder.setRoll(telemetryData.getRoll());
        builder.setYaw(telemetryData.getYaw());
        builder.setUltrasonicHeight(telemetryData.getUltrasonic());
        builder.setAltitudeHeight(telemetryData.getAltitude());
        builder.setAirspeed(telemetryData.getAirspeed());
        builder.setVelocity(telemetryData.getVelocity());
        builder.setTimestamps(telemetryData.getTimestamp());
        builder.setSource(TaskSource.SOURCE_YU_KONG.getMessage());
        builder.setPtPitch(telemetryData.getGimbalPitch());
        builder.setPtRoll(telemetryData.getGimbalRoll());
        builder.setPtYaw(telemetryData.getGimbalYaw());
        builder.setIsLive(telemetryData.getIsLive());
        TelemetryQueue.putTelemetry(projectId + "-&-yuKong-&-" + telemetryData.getTaskId(), builder.build());
        Telemetry.Data.Builder dataBuilder = Telemetry.Data.newBuilder();
        dataBuilder.setLatitude(telemetryData.getLat());
        dataBuilder.setLongitude(telemetryData.getLng());
        dataBuilder.setPitch(telemetryData.getPitch());
        dataBuilder.setRoll(telemetryData.getRoll());
        dataBuilder.setYaw(telemetryData.getYaw());
        dataBuilder.setUltrasonicHeight(telemetryData.getUltrasonic());
        dataBuilder.setAltitudeHeight(telemetryData.getAltitude());
        dataBuilder.setAirspeed(telemetryData.getAirspeed());
        dataBuilder.setVelocity(telemetryData.getVelocity());
        dataBuilder.setTimestamps(telemetryData.getTimestamp());
        dataBuilder.setPtPitch(telemetryData.getGimbalPitch());
        dataBuilder.setPtRoll(telemetryData.getGimbalRoll());
        dataBuilder.setPtYaw(telemetryData.getGimbalYaw());
        Telemetry.Data data = dataBuilder.build();
        jedisModel.rpush(appConstants.redisTelemetry + projectId + ":yuKong:" + telemetryData.getTaskId(), data.toByteArray());
    }

    private void dealTaskMessage(byte[] bytes) throws com.google.protobuf.InvalidProtocolBufferException {
        byte[] newBytes = new byte[bytes.length - 5];
        System.arraycopy(bytes, 5, newBytes, 0, bytes.length - 5);
        YuKongBusinessData.Data data = YuKongBusinessData.Data.parseFrom(newBytes);
        YuKongBusinessData.TaskData taskData = data.getTaskData();
        switch (data.getDataType()) {
            case DEFAULT_VALUE: {
                log.debug("DEFAULT_VALUE");
            }
            break;
            //任务开始
            case TASK_STATE_START: {
                doTaskStart(taskData);
            }
            break;
            //任务结束
            case TASK_STATE_FINISH: {
                //移除WebSocket遥测队列
                TelemetryQueue.telemetryQueueMap.remove(projectId + "-&-yuKong-&-" + taskData.getTaskId());
                doTaskFinish(taskData);
            }
            break;
            //开始推流
            case TASK_ISLIVE_ON: {
                webSocketService.sendAdminFlyTaskStatus(2, projectId, String.valueOf(taskData.getTaskId()), 3, taskData.getUavNum(), taskData.getUavName());
                webSocketService.sendTaskVideoLiveMessage(projectId, TaskSource.SOURCE_YU_KONG.getMessage(), String.valueOf(taskData.getTaskId()), 1);
                log.info("项目[" + projectId + "] 御空任务[ " + taskData.getTaskId() + " ]开始推流...");
            }
            break;
            case TASK_ISLIVE_OFF: {
                webSocketService.sendAdminFlyTaskStatus(2, projectId, String.valueOf(taskData.getTaskId()), 4, taskData.getUavNum(), taskData.getUavName());
                webSocketService.sendTaskVideoLiveMessage(projectId, TaskSource.SOURCE_YU_KONG.getMessage(), String.valueOf(taskData.getTaskId()), 0);
                log.info("项目[" + projectId + "] 御空任务[ " + taskData.getTaskId() + " ]结束推流...");
            }
            break;
            case TASK_ISPIC_ON: {
                log.debug("项目[" + projectId + "] 御空任务[ " + taskData.getTaskId() + " ]拍照...");
            }
            break;
            case TASK_VIDEO_STORAGE: {
                doTaskVideoStore(taskData);
            }
            break;
            default: {
                log.debug("项目[" + projectId + "] 御空 数据类型 -> " + data.getDataType().toString());
            }
        }
    }

    private void doTaskVideoStore(YuKongBusinessData.TaskData taskData) {
        log.info("项目[" + projectId + "] 御空任务[ " + taskData.getTaskId() + " ]视频存储结果...");
        log.info(""+taskData);
        if (taskData.getMediaType() == 2) {
            FlyTask dbFlyTask = flyTaskMapper.selectOne(new QueryWrapper<FlyTask>()
                    .eq("task_number", taskData.getTaskId())
                    .eq("task_source", TaskSource.SOURCE_YU_KONG.getValue()));
            if (Objects.isNull(dbFlyTask)) {
                log.error("项目[" + projectId + "] 御空飞行任务[ " + taskData.getTaskId() + " ] 视频记录错误,任务开始未记录...");
                TelemetryQueue.telemetryQueueMap.remove(projectId + "-&-yuKong-&-" + taskData.getTaskId());
                return;
            }
            FlyTask flyTask = new FlyTask();
            flyTask.setVideoUrl(taskData.getMediaUrl());
            final JSONObject jsonObject = new JSONObject();
            jsonObject.fluentPut("taskId", taskData.getTaskId())
                    .fluentPut("companyId", taskData.getCompanyId())
                    .fluentPut("mediaType", taskData.getMediaType())
                    .fluentPut("mediaUrl", taskData.getMediaUrl())
                    .fluentPut("mediaStartTime", taskData.getMediaStartTime())
                    .fluentPut("mediaEndTime", taskData.getMediaEndTime())
                    .fluentPut("mediaDuration", taskData.getMediaDuration())
                    .fluentPut("mediaStorage", taskData.getMediaStorage());
            final JSONArray historyVideos = dbFlyTask.getHistoryVideos();

            log.info(""+historyVideos);
            if (historyVideos != null) {
                final boolean allMatch = historyVideos.stream().allMatch(object -> {
                    final JSONObject video = JSONObject.parseObject(JSONObject.toJSONString(object));
                    log.info(""+video.getString("mediaUrl"));
                    log.info(""+taskData.getMediaUrl());
                    return video.getString("mediaUrl").equals(taskData.getMediaUrl());
                });
                log.info(""+allMatch);
                if (!allMatch) {
                    historyVideos.add(jsonObject);
                    flyTask.setHistoryVideos(historyVideos.toJSONString());
                }
            }else {
                flyTask.setHistoryVideos(new JSONArray().fluentAdd(jsonObject).toJSONString());
            }
            log.info(""+flyTask);
            flyTaskMapper.update(flyTask, new QueryWrapper<FlyTask>()
                    .eq("task_number", taskData.getTaskId()).eq("task_source", TaskSource.SOURCE_YU_KONG.getValue())
            );
            webSocketService.sendAdminFlyTaskStatus(2, projectId, String.valueOf(taskData.getTaskId()), 5, taskData.getUavNum(), taskData.getUavName());
            log.info("项目[" + projectId + "] 御空任务[ " + taskData.getTaskId() + " ]视频存储成功...");
        }
    }

    private void doTaskStart(YuKongBusinessData.TaskData taskData) {
        int taskId = taskData.getTaskId();
        FlyTask flyTask = new FlyTask();
        flyTask.setStartTime(new Date(taskData.getStartTime()));
        flyTask.setUavId(taskData.getUavNum());
        flyTask.setUavName(taskData.getUavName());
        flyTask.setTaskName(taskData.getTaskName());
        flyTask.setTaskNumber(String.valueOf(taskId));
        flyTask.setTaskSource(TaskSource.SOURCE_YU_KONG.getValue());
        flyTask.setFlightState(FlightState.FLIGHT_STATE_ON.getValue());
        flyTask.setProjectId(projectId);
        if (flyTaskMapper.insert(flyTask) != 1) {
            log.error("项目[" + projectId + "] 御空 无人机[" + taskData.getUavName() + "] 开始飞行任务[" + taskId + "] 记录失败");
        }
        webSocketService.sendAdminFlyTaskStatus(2, projectId, String.valueOf(taskId), 1, taskData.getUavNum(), taskData.getUavName());
        log.info("项目[" + projectId + "] 御空 无人机[" + taskData.getUavName() + "] 开始飞行任务[" + taskId + "]...");
    }

    private void doTaskFinish(YuKongBusinessData.TaskData taskData) {
        //枚举类型：TASK_VIDEO_STORAGE
        //
        //解析字段：TaskData
        //
        //名称	描述	类型	是否必须
        //taskId	任务编号	int32	是
        //companyId	公司id	int32	是
        //mediaType	1: 图片 2：直播视频	int32	是
        //mediaUrl	视频或图片地址	string	是
        //mediaStartTime	开始时间	int64	是
        //mediaEndTime	结束时间	int64	是
        //mediaDuration	时长，单位 秒	int64	是
        //mediaStorage	存储大小，单位 MB	double	是
        FlyTask dbFlyTask = flyTaskMapper.selectOne(new QueryWrapper<FlyTask>()
                .eq("task_number", taskData.getTaskId())
                .eq("task_source", TaskSource.SOURCE_YU_KONG.getValue()));
        if (Objects.isNull(dbFlyTask)) {
            log.error("项目[" + projectId + "] 御空飞行任务[ " + taskData.getTaskId() + " ] 任务开始未记录, 重建任务");
            dbFlyTask = new FlyTask();
            dbFlyTask.setTaskNumber(String.valueOf(taskData.getTaskId()));
            dbFlyTask.setTaskSource(TaskSource.SOURCE_YU_KONG.getValue());
            dbFlyTask.setStartTime(new Date(taskData.getStartTime()));
            dbFlyTask.setUavId(taskData.getUavNum());
            dbFlyTask.setUavName(taskData.getUavName());
            dbFlyTask.setTaskName(taskData.getTaskName());
            dbFlyTask.setFlightState(FlightState.FLIGHT_STATE_ON.getValue());
            dbFlyTask.setProjectId(projectId);
            flyTaskMapper.insert(dbFlyTask);
        }
        String storeTelemetryUrl = projectId + "/telemetry/" + dbFlyTask.getUavId().toLowerCase() + "/" + taskData.getTaskId() + ".pb";
        String taskTelemetryRedisKey = appConstants.redisTelemetry + projectId + ":yuKong:" + taskData.getTaskId();
        FlyTask flyTask = new FlyTask();
        flyTask.setFlightDuration(taskData.getFlightDuration());
        flyTask.setFlightDistance(taskData.getFlightMileage());
        flyTask.setFlightState(FlightState.FLIGHT_STATE_FINISH.getValue());
        flyTask.setEndTime(new Date(taskData.getEndTime()));
        webSocketService.sendAdminFlyTaskStatus(2, projectId, dbFlyTask.getTaskNumber(), 2, dbFlyTask.getUavId(), dbFlyTask.getUavName());
        log.info("项目[" + projectId + "] 御空 无人机[" + dbFlyTask.getUavName() + "] 结束飞行任务[" + taskData.getTaskId() + "]...");
        try {
            List<byte[]> bytesList = jedisModel.lrange(taskTelemetryRedisKey, 0, -1);
            Telemetry.DataList.Builder dataList = Telemetry.DataList.newBuilder();
            int size = bytesList.size();
            if (size == 0) {
                log.error("项目[" + projectId + "] 御空任务[" + taskData.getTaskId() + "]无飞行遥测数据，未上传OSS，遥测未存储!");
            } else {
                for (byte[] bytes0 : bytesList) {
                    dataList.addData(Telemetry.Data.parseFrom(bytes0));
                }
                Telemetry.Data endTelemetry = dataList.getDataList().get(dataList.getDataList().size() - 1);
                dataList.setEndLatitude(endTelemetry.getLatitude());
                dataList.setEndLongitude(endTelemetry.getLongitude());
                dataList.setEndTime(endTelemetry.getTimestamps());
                if (ocsSimpleModel.isObjectExist(storeTelemetryUrl)) {

                    final OcsObject object = ocsSimpleModel.getObject(storeTelemetryUrl);
                    Telemetry.DataList lastList = Telemetry.DataList.parseFrom(object.getInputStream());
                    Telemetry.DataList.Builder lastListBuilder = lastList.toBuilder();
                    lastListBuilder.addAllData(dataList.getDataList());
                    if (ocsSimpleModel.deleteObject(storeTelemetryUrl)) {
                        if (ossTelemetryUpload(taskData.getTaskId(), storeTelemetryUrl, taskTelemetryRedisKey, lastListBuilder, size)) {
                            flyTask.setTelemetryUrl("/" + storeTelemetryUrl);
                            Integer pipelineId = FlyTaskModel.getPipelineId(dataList, pipelineMapper, projectId);
                            flyTask.setPipelineId(pipelineId);
                            log.info("项目[" + projectId + "] 御空任务[" + taskData.getTaskId() + "] - 遥测追加[" + size + "]");
                            TelemetryQueue.telemetryQueueMap.remove(projectId + "-&-yuKong-&-" + dbFlyTask.getTaskNumber());
                        }
                    }
                } else {
                    Telemetry.Data startTelemetry = dataList.getDataList().get(0);
                    dataList.setProjectId(projectId);
                    dataList.setTaskId(String.valueOf(taskData.getTaskId()));
                    if (Objects.nonNull(dbFlyTask.getUavId())) {
                        dataList.setUavId(dbFlyTask.getUavId());
                    }
                    if (Objects.nonNull(dbFlyTask.getUavName())) {
                        dataList.setUavName(dbFlyTask.getUavName());
                    }
                    dataList.setSource(TaskSource.SOURCE_YU_KONG.getMessage());
                    dataList.setStartLatitude(startTelemetry.getLatitude());
                    dataList.setStartLongitude(startTelemetry.getLongitude());
                    dataList.setStartTime(startTelemetry.getTimestamps());
                    if (ossTelemetryUpload(taskData.getTaskId(), storeTelemetryUrl, taskTelemetryRedisKey, dataList, size)) {
                        flyTask.setTelemetryUrl("/" + storeTelemetryUrl);
                        TelemetryQueue.telemetryQueueMap.remove(projectId + "-&-yuKong-&-" + dbFlyTask.getTaskNumber());
                    }
                    Integer pipelineId = FlyTaskModel.getPipelineId(dataList, pipelineMapper, projectId);
                    if (Objects.nonNull(pipelineId)) {
                        flyTask.setPipelineId(pipelineId);
                        log.info("项目[" + projectId + "] 御空任务[" + taskData.getTaskId() + "] " + "已设置归属管线[" + pipelineId + "]");
                    }
                }
            }
            flyTaskMapper.update(flyTask, new QueryWrapper<FlyTask>()
                    .eq("task_number", taskData.getTaskId()).eq("task_source", TaskSource.SOURCE_YU_KONG.getValue())
            );
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean ossTelemetryUpload(int taskId, String storeTelemetryUrl, String taskTelemetryRedisKey, Telemetry.DataList.Builder dataList, int size) {
        if (ocsSimpleModel.putObject(storeTelemetryUrl, dataList.build().toByteArray())) {
            log.info("项目[" + projectId + "] 御空任务[" + taskId + "] - 遥测数据共计 - [" + size + "] 条，已上传OSS!");
            Long del = jedisModel.del(taskTelemetryRedisKey);
            if (1 == del) {
                log.info("Redis取出遥测 [" + taskTelemetryRedisKey + "] count -> [" + size + "], 并删除...");
            } else {
                log.error("Redis删除遥测异常！");
            }
            UploadFile uploadFile = new UploadFile();
            uploadFile.setCategory(FileTypeEnum.FILE_TELEMETRY_PB.getValue());
            uploadFile.setUrl("/" + storeTelemetryUrl);
            uploadFile.setSize(ocsSimpleModel.getObject(storeTelemetryUrl).getContentLength());
            uploadFile.setType("application/octet-stream");
            uploadFile.setProjectId(projectId);
            if (1 == uploadFileMapper.insert(uploadFile)) {
                log.info("上传文件[{}]已记录到数据库", "/" + storeTelemetryUrl);
            } else {
                log.error("上传文件[{}]数据库未记录", "/" + storeTelemetryUrl);
            }
            return true;
        } else {
            log.error("遥测上传OSS异常！");
            return false;
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ByteBuf buffer = Unpooled.buffer();
        buffer.writeByte(0x01);
        buffer.writeInt(token.getBytes().length);
        buffer.writeBytes(token.getBytes());
        byte[] bytes = new byte[buffer.readableBytes()];
        buffer.readBytes(bytes);
        ctx.channel().writeAndFlush(bytes);
        buffer.release();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleState state = ((IdleStateEvent) evt).state();
            if (state == IdleState.WRITER_IDLE) {
                ByteBuf buffer = Unpooled.buffer();
                buffer.writeByte(0x02);
                buffer.writeInt(8);
                buffer.writeLong(System.currentTimeMillis());
                byte[] bytes = new byte[buffer.readableBytes()];
                buffer.readBytes(bytes);
                ctx.channel().writeAndFlush(bytes);
                buffer.release();
            }
        }
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        log.error("Project[" + projectId + "] yk tcp channel unregistered");
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                final QueryMo queryMo = new QueryMo();
                queryMo.put("id", projectId);
                queryMo.put("ykAccessAvailable", 0);
                queryMo.build(Project.class, null);
                if ((projectService.selectCount(queryMo) == 1)) {
                    Runnable runnable = () -> new YkClient(
                            true,
                            projectId,
                            ykAccessKey,
                            ykAccessSecret,
                            webSocketService,
                            flyTaskMapper,
                            pipelineMapper,
                            uploadFileMapper,
                            projectService,
                            ocsSimpleModel,
                            jedisModel,
                            appConstants);
                    Constants.EXECUTOR.execute(runnable);
                }
                log.info("project[" + projectId + "] reconnect yk-server");
            }
        }, 2000);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.error("Project[" + projectId + "] yk tcp channel inactive");
        super.channelInactive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.channel().close();
    }
}